package cn.ratel.kit;

import cn.ratel.db.Model;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.runtime.RuntimeSchema;

import java.util.Map;

public class SerializerKit {

	public static <T> T toObject(byte[] bytes, Class<T> clazz) {
		return Factory.parser.toObject(bytes, clazz);
	}

	public static <T> byte[] toBytes(T obj) {
		return Factory.parser.toBytes(obj);
	}

	public static class Factory {
		private static Parser parser = new Parser() {
			@Override
			public <T> T toObject(byte[] bytes, Class<T> clazz) {
				if (bytes == null) {
					return null;
				}
				T obj;
				try {
					obj = clazz.newInstance();
					ProtobufIOUtil.mergeFrom(bytes, obj, RuntimeSchema.getSchema(clazz));
				} catch (InstantiationException | IllegalAccessException e) {
					throw new RuntimeException("can not create instance of : " + clazz.getName(), e);
				}
				return obj;
			}

			@Override
			public <T> byte[] toBytes(T obj) {
				if (obj == null) {
					return null;
				}
				Class<T> clazz = (Class<T>) obj.getClass();
				if (obj instanceof Model) {
					Map<String, Object> attrsColumnOnly = ((Model) obj).attrsColumnOnly();
					T model;
					try {
						model = clazz.newInstance();
					} catch (InstantiationException | IllegalAccessException e) {
						throw new RuntimeException("can not create instance of : " + clazz.getName(), e);
					}
					((Model) model).put(attrsColumnOnly);
					obj = model;
				}
				return ProtobufIOUtil.toByteArray(obj, RuntimeSchema.getSchema(clazz), LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
			}

		};

		public static void setParser(Parser parser) {
			Factory.parser = parser;
		}
	}

	public interface Parser {
		<T> T toObject(byte[] bytes, Class<T> clazz);

		<T> byte[] toBytes(T obj);
	}

}
